﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BE.Web.Betfair.Interface;
using BE.Web.Betfair.EBC.BetfairGlobal;
using BE.Security;
using BE.Web.Betfair.EBC.BetfairExchange;
using BE.Web.Betfair.Interface.Entities;
using BE.Web.Betfair.Interface.Requests;
using System.Diagnostics.Contracts;
using System.Collections.ObjectModel;
using BE.EBC;

namespace BE.Web.Betfair.EBC
{
    /// <summary>
    /// Event based Betfair component
    /// </summary>
    /// <seealso href="http://bdp.betfair.com/"/>
    public sealed class BetfairEBC : IBetfairAdapter,IDisposable
    {
        #region IBetfairAdapter Members
        /// <summary>
        /// Objects the invariant.
        /// </summary>
        [ContractInvariantMethod]
        void ObjectInvariant()
        {
            Contract.Invariant(this.client != null);
            Contract.Invariant(this.exChangeclient != null);
        }

        BetfairGlobal.BFGlobalServiceClient client;
        BetfairExchange.BFExchangeServiceClient exChangeclient;


        /// <summary>
        /// Initializes a new instance of the <see cref="BetfairEBC"/> class.
        /// </summary>
        public BetfairEBC()
        {
            #region conditions
            Contract.Ensures(this.client != null);
            Contract.Ensures(this.exChangeclient != null);
            #endregion

            this.client = new BFGlobalServiceClient();
            this.exChangeclient = new BFExchangeServiceClient();

        }
        /// <summary>
        /// BetFair Product Request
        /// </summary>
        /// <param name="credentials"></param>
        /// <param name="product"></param>
        /// <returns></returns>
        public Session Login(BE.Common.User.Credentials credentials, BetfairProduct product)
        {
            this.raiseTrace("Login", EBCTraceLevel.Trace);

            Session result;
            LoginReq request = new LoginReq();


            request.username = credentials.UserName;
            request.password = credentials.Password.ToPlainString();

            request.productId = product.Id;

            LoginResp response = client.login(request);

            if (response == null || response.header == null)
            {
                throw new InvalidOperationException();
            }

            result = new Session(credentials.UserName,response.header.sessionToken);

            if (response.errorCode == LoginErrorEnum.OK)
            {
                result.Authenticated = true;
            }
            else
            {
                result.Authenticated = false;
            }
            
            return result;
        }

        

        /// <summary>
        /// Logout
        /// </summary>
        /// <param name="session"></param>
        public void Logout(Session session)
        {
            this.raiseTrace("Logout", EBCTraceLevel.Trace);
            LogoutReq request = new LogoutReq();
            request.header = session.ToGlobaAPIRequestHeader();

            LogoutResp response = client.logout(request);


            if (response != null)
            {
            }

        }

        /// <summary>
        /// Retrieves all Markets
        /// </summary>
        /// <param name="session">The session for the request</param>
        /// <returns>Returns all Markets</returns>
        public System.Collections.ObjectModel.ReadOnlyCollection<BE.Web.Betfair.Interface.Entities.BetMarket> GetAllMarkets(Session session)
        {
            this.raiseTrace("GetAllMarkets", EBCTraceLevel.Trace);

            
            BetfairExchange.GetAllMarketsReq request = new BetfairExchange.GetAllMarketsReq();
            request.header = session.ToExchangeAPIRequestHeader();
            request.fromDate = DateTime.Now.AddDays(-14);

            string[] countries = new string[1]{"DEU"};

            request.countries = countries;
            GetAllMarketsResp response = exChangeclient.getAllMarkets(request);

            if (response != null)
            {
            }

            return new System.Collections.ObjectModel.ReadOnlyCollection<BE.Web.Betfair.Interface.Entities.BetMarket>(new List<BE.Web.Betfair.Interface.Entities.BetMarket>());
        }

        /// <summary>
        /// Input pin for cached Events.
        /// </summary>
        public event Func<IEnumerable<BetEventType>> In_EventsFromCache;

        /// <summary>
        /// Gets the events from cache.
        /// </summary>
        /// <returns></returns>
        private IEnumerable<BetEventType> getEventsFromCache()
        {
            IEnumerable<BetEventType> result = null;

            Func<IEnumerable<BetEventType>> handler = In_EventsFromCache;

            if (handler != null)
            {
                result = handler();
            }

            return result;
        }

        /// <summary>
        /// Retrieves all events
        /// </summary>
        /// <param name="session"></param>
        /// <returns></returns>
        public System.Collections.ObjectModel.ReadOnlyCollection<Interface.Entities.BetEventType> GetAllEventTypes(Session session)
        {
            this.raiseTrace("GetAllEventTypes", EBCTraceLevel.Trace);

            List<BetEventType> result = new List<BetEventType>(10);

            GetEventTypesReq request = new GetEventTypesReq();
            request.header = new BetfairGlobal.APIRequestHeader();
            request.header.sessionToken = session.Token;

            GetEventTypesResp response = client.getActiveEventTypes(request);

            if (response != null && response.eventTypeItems != null)
            {
                BetEventType curElement;

                foreach (EventType curEvent in response.eventTypeItems)
                {
                    if (curEvent != null && curEvent.name != null)
                    {
                        curElement = new BetEventType(curEvent.id, curEvent.name, curEvent.nextMarketId, curEvent.exchangeId);
                        result.Add(curElement);
                    }

                }

            }

            return new System.Collections.ObjectModel.ReadOnlyCollection<BetEventType>(result);
        }



        /// <summary>
        /// Gets the type of the events of.
        /// </summary>
        /// <param name="eventRequest">The event request.</param>
        public void GetEventsOfType(BetEventByTypeRequest eventRequest)
        {
            this.raiseTrace("GetEventsOfType", EBCTraceLevel.Trace);

            GetEventsReq request = new GetEventsReq();
            request.eventParentId = eventRequest.EventType.Id;
            request.header = eventRequest.Session.ToGlobaAPIRequestHeader();
            

            GetEventsResp response = client.getEvents(request);

            List<BetEvent> events = null;
            if(response != null && response.eventItems != null)
            {
                events = new List<BetEvent>(response.eventItems.Length);

                BetEvent element;


                if (response.marketItems != null && response.marketItems.Length >0)
                {
                    foreach (MarketSummary curMarkt in response.marketItems)
                    {
                        Console.WriteLine(curMarkt);
                    }
                }
                foreach (BFEvent curEvent in response.eventItems)
                {
                    if (curEvent != null && curEvent.eventName != null)
                    {
                        List<BetMarketSummary> subElements = getMarketFromEventObject(response);

                        element = new BetEvent(curEvent.eventId, curEvent.eventName, eventRequest.EventType, curEvent.startTime, curEvent.orderIndex, curEvent.timezone, subElements);
                        events.Add(element);
                    }

                }
            }

            eventRequest.Return(new System.Collections.ObjectModel.ReadOnlyCollection<BetEvent>(events));
        }


        private static List<BetMarketSummary> getMarketFromEventObject(GetEventsResp response)
        {
            #region conditions
            Contract.Requires<ArgumentNullException>(response != null);
            Contract.Ensures(Contract.Result<List<BetMarketSummary>>() != null);
            #endregion

            List<MarketSummary> markets;
            List<BetMarketSummary> subElements = new List<BetMarketSummary>();

            if (response != null && response.marketItems != null && response.marketItems.Length > 0)
            {
                markets = response.marketItems.ToList<MarketSummary>();

                BetMarketSummary subElement;
                foreach (MarketSummary curSummary in markets)
                {
                    
                    subElement = ToBetMarketSummary(curSummary);
                    subElements.Add(subElement);
                }
            }
            return subElements;
        }

        private static BetMarketSummary ToBetMarketSummary(MarketSummary curSummary)
        {
            BetMarketSummary subElement;
            subElement = new BetMarketSummary(curSummary.marketId,curSummary.exchangeId, curSummary.marketName);
            return subElement;
        }

        

       
        /// <summary>
        /// Seends a Keepalive signal
        /// </summary>
        /// <param name="session"></param>
        public void KeepAlive(Session session)
        {
            this.raiseTrace("KeepAlive", EBCTraceLevel.Trace);
            KeepAliveReq request = new KeepAliveReq();
            request.header = session.ToGlobaAPIRequestHeader();

            client.keepAlive(request);
        }

        /// <summary>
        /// Retrieves subevents
        /// </summary>
        /// <param name="request">ReadRequest</param>
        public void AddMarketsAndSubEvents(BetEventByParentEventRequest request)
        {
            this.raiseTrace("GetSubEvents", EBCTraceLevel.Trace);

            GetEventsReq dataRequest = new GetEventsReq();
            dataRequest.eventParentId = request.ParentEvent.Id;
            dataRequest.header = request.Session.ToGlobaAPIRequestHeader();

            GetEventsResp response = client.getEvents(dataRequest);
            List<BetEvent> result = new List<BetEvent>();
            if (response != null)
            {

              
                List<BetMarketSummary> subElements = getMarketFromEventObject(response);
                request.ParentEvent.AssociatedMarkets.AddRange(subElements);

                if (response.eventItems != null)
                {
                    BetEvent element;


                    foreach (BFEvent curEvent in response.eventItems)
                    {
                        if (curEvent != null)
                        {
                            element = new BetEvent(curEvent.eventId, curEvent.eventName, request.ParentEvent.EventType, curEvent.startTime, curEvent.orderIndex, curEvent.timezone, subElements);
                            result.Add(element);
                        }
                    }
                }
            }
            request.Return(new System.Collections.ObjectModel.ReadOnlyCollection<BetEvent>(result));
        }

      

        /// <summary>
        /// Requests the funding informations of a user
        /// </summary>
        /// <param name="request"></param>
        public void GetUserFund(UserFundsRequest request)
        {
            this.raiseTrace("GetUserFund", EBCTraceLevel.Trace);
            GetAccountFundsReq internalRequest = new GetAccountFundsReq();
            internalRequest.header = request.Session.ToExchangeAPIRequestHeader();

            GetAccountFundsResp response = exChangeclient.getAccountFunds(internalRequest);

            FundInfo nfo = new FundInfo(response.balance, response.availBalance);

            request.Return(nfo);
        }

        
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            this.raiseTrace("Dispose", EBCTraceLevel.Trace);
            this.client.Close();
            this.exChangeclient.Close();
        }


        /// <summary>
        /// Resolves the Markets of the given event
        /// </summary>
        /// <param name="request">ReadRequest</param>
        public void GetMarketsOfEvent(EventMarketsRequest request)
        {
            this.raiseTrace("GetMarketsOfEvent - Currently Not Implemented", EBCTraceLevel.Trace);
        }


        /// <summary>
        /// Raises the Trace output pin
        /// </summary>
        /// <param name="message">the message which should be traces</param>
        /// <param name="traceLevel">the tracelevel of the message</param>
        private void raiseTrace(string message,EBCTraceLevel traceLevel)
        {
            Action<object, string, EBCTraceLevel> handler = this.Out_Trace;

            if (handler != null)
            {
                handler(this, message, traceLevel);
            }
        }

      
        /// <summary>
        /// Outputpin for traces messages
        /// </summary>
        public event Action<object, string, EBCTraceLevel>Out_Trace;



        /// <summary>
        /// Requests the Bets of a given Market
        /// </summary>
        /// <param name="request"></param>
        public void GetBetsOfMarket(BetsByMarketRequest request)
        {
            GetCurrentBetsReq betRequest= new GetCurrentBetsReq();
            betRequest.header = request.Session.ToExchangeAPIRequestHeader();

            betRequest.marketId = request.BetMarket.Id;
            betRequest.detailed = true;

            GetCurrentBetsResp response = exChangeclient.getCurrentBets(betRequest);
            List<BE.Web.Betfair.Interface.Entities.Bet> result = new List<Interface.Entities.Bet>();

            if (response != null && response.bets != null)
            {

                BE.Web.Betfair.Interface.Entities.Bet curElement;

                foreach (BE.Web.Betfair.EBC.BetfairExchange.Bet curBet in response.bets)
                {
                    
                    if (curBet != null)
                    {

                        curElement = new Interface.Entities.Bet(curBet.betId, curBet.marketName, curBet.betType.ToString());
                        result.Add(curElement);
                    }
                }
            }
            request.Return(new ReadOnlyCollection<Interface.Entities.Bet>(result));
        }


        /// <summary>
        /// Requests the prices of a market
        /// </summary>
        /// <param name="request"></param>
        public void GetMarketPrices(MarketPriceRequest request)
        {
            GetMarketPricesReq betRequest = new GetMarketPricesReq();
            betRequest.header = request.Session.ToExchangeAPIRequestHeader();
            betRequest.marketId = request.MarketId;

            GetMarketPricesResp response = exChangeclient.getMarketPrices(betRequest);

            if (response != null)
            { 

            }

        }

        #endregion
    }
}

